package dom

import (
	"io"
	"os"
	"fmt"
)

const (
	Indent  = "\t"
	NewLine = "\n"
)

type Writer struct {
	writer io.Writer
	depth  int
	debug  bool
	ns     map[string]string
}

func NewWriter(writer io.Writer, debug bool, nsmap map[string]string) *Writer {
	return &Writer{writer: writer, debug: debug, ns: nsmap}
}

func (wr *Writer) Debug() bool {
	return wr.debug
}

func (wr *Writer) Print(a ...interface{}) {
	if _, ok := fmt.Fprint(wr.writer, a...); ok == nil {
		return
	} else {
		panic(ok)
	}

	panic("Unreachable")
}

func (wr *Writer) WriteIndent() {
	if !wr.debug {
		return
	}
	str := ""
	for i := 0; i < wr.depth; i++ {
		str += Indent
	}

	wr.Print(str)
}

func (wr *Writer) IndentPrint(a ...interface{}) {
	wr.WriteIndent()
	wr.Print(a...)
}

func (wr *Writer) Printf(format string, a ...interface{}) (n int, errno os.Error) {
	if _, ok := fmt.Fprintf(wr.writer, format, a...); ok == nil {
		return
	} else {
		panic(ok)
	}

	panic("Unreachable")
}

func (wr *Writer) IndentPrintf(format string, a ...interface{}) {
	wr.WriteIndent()
	wr.Printf(format, a...)
}

func (wr *Writer) NewLineOpt() {
	if wr.debug {
		wr.Print("\n")
	}
}

func (wr *Writer) Indent() {
	wr.depth++
}

func (wr *Writer) Exdent() {
	wr.depth--
	if wr.depth < 0 {
		wr.depth = 0
	}
}

func (wr *Writer) WriteNamespaced(tag, ns string) {
	//TODO: use ns
	wr.Print(tag)
}

type XmlDomElement interface {
	XmlWrite(writer *Writer)
}

type Element struct {
	tag        string
	id         string
	attributes map[string]string
	children   []XmlDomElement
}

func (e *Element) XmlWrite(writer *Writer) {
	str := "<" + e.tag
	if e.id != "" {
		str += ` id="` + e.id + `"`
	}

	for key, val := range e.attributes {
		str += " " + key + `="` + val + `"`
	}

	if e.children != nil && len(e.children) > 0 {
		str += ">"
		writer.IndentPrint(str)
		writer.NewLineOpt()
		writer.Indent()
		for _, val := range e.children {
			val.XmlWrite(writer)
			writer.NewLineOpt()
		}
		writer.Exdent()
		writer.IndentPrint("</" + e.tag + ">")
	} else {
		str += "/>"
		writer.IndentPrint(str)
	}
}

func MakeElement(tag string) *Element {
	return &Element{tag: tag, attributes: make(map[string]string), children: nil}
}

func MakeElementNs(tag, ns string) *Element {
	return MakeElement(tag)
}

func (e *Element) Child(child XmlDomElement) *Element {
	if e.children == nil {
		e.children = append(make([]XmlDomElement, 0), child)
	} else {
		e.children = append(e.children, child)
	}

	return e
}

func (g *Element) AddToAttribute(attribute, add string) {
	if val, ok := g.attributes[attribute]; ok {
		add = val + " " + add
	}

	g.attributes[attribute] = add
}

func (g *Element) AddToNamespacedAttribute(attribute, ns, add string) {
	//TODO: do something meaningful with ns
	
	switch ns {
	case `http://www.w3.org/2000/svg`:
		ns = "svg"
	}
	
	g.AddToAttribute(ns + ":" + attribute, add)
}

func (g *Element) SetAttribute(attribute, value string) {
	g.attributes[attribute] = value
}

func (g *Element) SetNamespacedAttribute(attribute, ns, value string) {
	g.SetAttribute(attribute, value)
}

func (g *Element) Class(value string) {
	g.AddToAttribute("class", value)
}

func (g *Element) Id(value string) {
	g.AddToAttribute("id", value)
}

